import logging as log
import os
import subprocess
import sys
import yaml
import time
import datetime
import socket
from mongo_connect import get_connect


def yaml_load(filename_to_load):
    with open(filename_to_load, 'r') as stream:
        try:
            return yaml.safe_load(stream)
        except yaml.YAMLError as e:
            log.exception(str(e))
            return


def get_command(mongodump_path='./mongodb/bin',
                mongodump_command_name='mongodump'):
    file_full_path = '{mongodump_path}/{mongodump_command_name}'.format(
        mongodump_path=mongodump_path,
        mongodump_command_name=mongodump_command_name)
    try:
        if os.path.exists(file_full_path):
            return file_full_path
        else:
            return mongodump_command_name
    except Exception:
        return mongodump_command_name


def run_cmd(cmd, information=None, file_output=None):
    """
    执行命令并发送初始信息给mq
    :param cmd: 执行的备份命令
    :param information: 备份信息实例
    :return:
    """
    log.debug(cmd)
    if file_output is None:
        stdout = subprocess.PIPE
        stderr = subprocess.STDOUT
    else:
        stdout = file_output
        stderr = file_output
    p = subprocess.Popen(cmd,
                         shell=True,
                         stdout=stdout,
                         stderr=stderr)
    pid = p.pid
    try:
        information.set_pid_info(pid=pid)
        information.backup_startup_publish()
    except AttributeError as e:
        pass
    # result = p.stdout.read()
    return_code = p.wait()
    return return_code


def make_dir(path_to_make):
    try:
        if not os.path.exists(path_to_make):
            os.makedirs(path_to_make)
    except Exception as e:
        log.exception(str(e))


def get_current_format_time():
    now_time = time.localtime(time.time())
    format_time = time.strftime('%Y%m%d%H%M', now_time)
    return format_time


def time_stamp_to_file(file_name):
    with open(file_name, 'w') as of:
        of.write(str(int(time.time())))


def get_host_port_by_string(instance_string):
    try:
        connect_instance_list = instance_string.split(',')
        connect_host = connect_instance_list[0].split(':')[0]
        connect_port = connect_instance_list[0].split(':')[1]
        return connect_host, int(connect_port)
    except Exception as e:
        sys.exit(1)


def get_ip_address():
    # return "10.200.10.220"

    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.connect(("8.8.8.8", 80))
    local_real_ip_address = s.getsockname()[0]
    s.close()
    return local_real_ip_address


def get_mongodb_restore_command(**kwargs):
    host = kwargs.get('host')
    port = kwargs.get('port')

    user = kwargs.get('user')
    password = kwargs.get('password')
    gzip = kwargs.get('gzip', 1)
    num_parallel_collections = kwargs.get('num_parallel_collections', 4)
    if gzip == 1:
        gzip_string = " --gzip"
        file_postfix = '.gz'
    else:
        gzip_string = ""
        file_postfix = ""
    authentication_database = kwargs.get('authentication_database', 'admin')
    backup_data_path = kwargs.get('backup_data_path', '/data/mongodb_backup/')
    mongo_restore_command = kwargs.get('mongo_restore_command', 'mongorestore')

    database = kwargs.get('database')
    collection = kwargs.get('collection')
    if database is None:
        # database_string = ""
        # collection_string = ""
        sys.exit(1)
    else:
        database_string = "-d {database}".format(database=database)
        if collection is None:
            collection_string = ""
            mongodump_restore_full_command = """{mongo_restore_command} -u {user} -p '{password}' --authenticationDatabase {authentication_database} \
             --host  {host} --port {port} --numParallelCollections={num_parallel_collections} {gzip_string}\
              {database_string} {collection_string} {backup_data_path}/{database}/""".format(
                mongo_restore_command=mongo_restore_command,
                user=user,
                password=password,
                authentication_database=authentication_database,
                host=host,
                port=port,
                num_parallel_collections=num_parallel_collections,
                gzip_string=gzip_string,
                database_string=database_string,
                collection_string=collection_string,
                collection=collection,
                database=database,
                backup_data_path=backup_data_path)
        else:
            collection_string = "-c {collection}".format(collection=collection)
            mongodump_restore_full_command = """{mongo_restore_command} -u {user} -p '{password}' --authenticationDatabase {authentication_database} \
             --host  {host} --port {port} --numParallelCollections={num_parallel_collections} {gzip_string}\
              {database_string} {collection_string} {backup_data_path}/{database}/{collection}.bson{file_postfix}""".format(
                mongo_restore_command=mongo_restore_command,
                user=user,
                password=password,
                authentication_database=authentication_database,
                host=host,
                port=port,
                num_parallel_collections=num_parallel_collections,
                gzip_string=gzip_string,
                database_string=database_string,
                collection_string=collection_string,
                file_postfix=file_postfix,
                collection=collection,
                database=database,
                backup_data_path=backup_data_path)
    return mongodump_restore_full_command


def get_mongo_dump_backup_command(**kwargs):
    host = kwargs.get('host')
    port = kwargs.get('port')
    read_prefer = kwargs.get("read_prefer", " --readPreference 'secondary' ")
    user = kwargs.get('user')
    password = kwargs.get('password')
    gzip = kwargs.get('gzip', 1)
    oplog = kwargs.get('gzip', 0)
    if oplog == 1:
        oplog_str = " --oplog "
    else:
        oplog_str = ""
    num_parallel_collections = kwargs.get('num_parallel_collections', 1)
    if gzip == 1:
        gzip_string = "--gzip"
    else:
        gzip_string = ""
    authentication_database = kwargs.get('authentication_database', 'admin')
    backup_data_path = kwargs.get('backup_data_path', '/data/mongodb_backup/')
    mongodump_command = kwargs.get('mongodump_command', 'mongodump')

    database = kwargs.get('database')
    collection = kwargs.get('collection')
    if database is None:
        database_string = ""
        collection_string = ""
    else:
        database_string = "-d {database}".format(database=database)
        if collection is None:
            collection_string = ""
        else:
            collection_string = "-c {collection}".format(collection=collection)
    mongodump_backup_command = """{mongodump_command} -u {user} -p '{password}' --authenticationDatabase {authentication_database} \
     --host  {host} --port {port} --numParallelCollections={num_parallel_collections} {oplog_str} {gzip_string}\
      {database_string} {collection_string} {read_prefer} --out={backup_data_path}""".format(
        mongodump_command=mongodump_command,
        user=user,
        password=password,
        authentication_database=authentication_database,
        host=host,
        port=port,
        num_parallel_collections=num_parallel_collections,
        gzip_string=gzip_string,
        database_string=database_string,
        collection_string=collection_string,
        backup_data_path=backup_data_path,
        oplog_str=oplog_str,
        read_prefer=read_prefer)
    return mongodump_backup_command


def run_cmd_and_log(cmd, run_log_file=None):
    # run_log_file = "{basedir}/run_result.log".format(basedir=basedir)
    try:
        file_output = open(run_log_file, "a")
    except Exception as e:
        file_output = None
    return_code = run_cmd(cmd, file_output=file_output)
    if return_code != 0:
        log.info("Error: return_code:{return_code}. please check in {run_log_file}".format(return_code=return_code,
                                                                                           run_log_file=run_log_file))


def get_last_year_month():
    today = datetime.date.today()
    first = today.replace(day=1)
    last_month = first - datetime.timedelta(days=1)

    last_year_month = last_month.strftime("%Y-%m")
    return last_year_month.split('-')[0], last_year_month.split('-')[1]


def get_file_last_n_line(file_name, n=1):
    assert n >= 0

    pos = n + 1
    lines = []

    with open(file_name) as f:
        while len(lines) <= n:
            try:

                f.seek(-pos, 2)
            except IOError:
                f.seek(0)
                break
            finally:
                lines = list(f)
            pos *= 2
    return lines[-n:]


def get_last_month_year_database(database_list):
    last_month_year, last_month_month = get_last_year_month()
    list_to_return = []
    for database in database_list:
        if database.endswith(last_month_year):
            list_to_return.append(database)
    return list_to_return


def get_last_month_month_collection(collections_list):
    last_month_year, last_month_month = get_last_year_month()
    list_to_return = []
    for collection in collections_list:
        if collection.endswith(last_month_month):
            list_to_return.append(collection)
    return list_to_return


def get_match_data(data_list, year_or_month):
    last_month_year, last_month_month = get_last_year_month()
    if year_or_month == "year":
        end_string_pattern = last_month_year
    elif year_or_month == "month":
        end_string_pattern = last_month_month
    else:
        log.error("year or month argument error .")
        return []
    list_to_return = []
    for db_coll in data_list:
        if db_coll.endswith(end_string_pattern):
            list_to_return.append(db_coll)
    return list_to_return


def get_all_shards(mongo_connect):
    if mongo_connect.is_mongos is True:
        return mongo_connect.admin.command('listShards')
    else:
        return []
